package 网络编程项目;

import 网络编程项目.login.*;
import 网络编程项目.login.utiles.JsonUtil;
import 网络编程项目.pojo.Message;
import 网络编程项目.pojo.MessageType;
import 网络编程项目.pojo.Response;
import 网络编程项目.util.ObjectUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;

/**
 * 先看Server代码。。。
 * 需要任何成员变量都可以加，实体类的属性也自己加
 */
public class Client extends JFrame {

    private final int DEFAULT_BUFFER_SIZE = 1024;
    private final int PORT = 9100;
    private final String HOST = "127.0.0.1";

    private SocketChannel channel;

    private Selector selector;

    private ByteBuffer buffer;

    private ListenerThread listenerThread;

    private String username;
    
       private JList<String> onlineUserList;

    private JTextArea messageArea;

    private JTextArea inputArea;

    private JLabel count_label;


//测试git
    public Client() throws IOException {
        initNet();
        initUI();
    }

    /**
     * 创建GUI，绑定事件，添加功能
     */
    private void initUI(){

        //TODO  GUI设计的代码写在这里面
        Login.main(channel);


    }

    private void createMainUI(){
        //设置窗口位置和大小
        setBounds(600,100,780,510);
        //设置窗口关闭规则
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        Container c=getContentPane();
        c.setLayout(null);//设置绝对布局
        this.setTitle("正大光明聊天室");     //设置窗口名字
        JButton[] a = new JButton[20];
        for(int i=0;i<=a.length-1;i++){
            a[i]=new JButton();
            c.add(a[i]);
        }
        a[0].setText("查看群文件");
        a[0].setBounds(5,325,100,25);

        a[1].setText("上传文件");
        a[1].setBounds(109,325,86,25);
        a[2].setText("个人中心");
        a[2].setBounds(199,325,86,25);
        a[3].setText("发送");
        a[3].setBounds(305,325,86,25);
        a[4].setText("清空");
        a[4].setBounds(400,325,86,25);

        a[0].addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
              requestFiles();
            }
        });
        a[1].addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                upload();
            }
        });
        a[2].addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                PersonalCenter.main(JsonUtil.StringToUser(listenerThread.getLoginResponse().getMsg()));
            }
        });
        a[3].addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                try {
                    broadCastMessage();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });
        a[4].addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                inputArea.setText("");
            }
        });

        a[8].setText("在线成员");
        a[8].setBounds(510,0,86,25);
        a[8].setEnabled(false);

        count_label = new JLabel("1");
        count_label.setBounds(620,0,100,25);
        add(count_label);

        a[9].addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                requestOnlineUsers();
            }
        });

        messageArea = new JTextArea();//设置文本框
        messageArea.setBounds(0,0,500,325);
        messageArea.setLineWrap(true);
        messageArea.setEditable(false);
        messageArea.append("欢迎大家来到兰亭569公共聊天室，请大家注意言辞，规范聊天。");
        messageArea.append("\n\n");
        JScrollPane jsp1 = new JScrollPane(messageArea);
        jsp1.setBounds(0,0,500,325);
        jsp1.setViewportView(messageArea);
        c.add(jsp1);

        inputArea = new JTextArea();//设置文本框
        inputArea.setBounds(0,350,500,100);
        inputArea.setLineWrap(true);
        JScrollPane jsp2 = new JScrollPane(inputArea);
        jsp2.setBounds(0,350,500,100);
        jsp2.setViewportView(inputArea);
        c.add(jsp2);


        String[] contents = {"刘恩光", "张云瑞", "王兵坤", "寇子时","张少辉","姚燕","王佳庆",
                "刘恩光", "张云瑞", "王兵坤", "寇子时","张少辉","姚燕","王佳庆",
                "刘恩光", "张云瑞", "王兵坤", "寇子时","张少辉","姚燕","王佳庆",
                "刘恩光", "张云瑞", "王兵坤", "寇子时","张少辉","姚燕","王佳庆",
                "刘恩光", "张云瑞", "王兵坤", "寇子时","张少辉","姚燕","王佳庆"};//输出群成员
        //列表中需要放入内容
        onlineUserList = new JList(contents);
        JPanel jPanel = new JPanel();
        JLabel jLabel = new JLabel("测hi");
        JButton button = new JButton("私聊");
        jPanel.add(jLabel);
        jPanel.add(button);
        onlineUserList.add(jPanel);
        onlineUserList.setBounds(510,30,230,410);
        c.add(onlineUserList);

        //设置弹出菜单
        JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem item = new JMenuItem("私聊");
        popupMenu.add(item); //添加菜单项Open
        onlineUserList.add(popupMenu);

       item.addActionListener(new ActionListener() {
           @Override
           public void actionPerformed(ActionEvent e) {
               String receiver = onlineUserList.getSelectedValue();
               System.out.println("即将打开和"+receiver+"的私聊对话框");
               new ToOneArea(username,receiver);
           }
       });

        //设置滚动条
        JScrollPane jsp = new JScrollPane(onlineUserList);
        jsp.setBounds(510,30,220,410);
        c.add(jsp);

        onlineUserList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getButton()==3&&onlineUserList.getSelectedIndex()>=0)
                    popupMenu.show(onlineUserList,e.getX(),e.getY());
            }
        });

        requestOnlineUsers();

        setVisible(true);//设置窗口可见
    }



    private void initNet() throws IOException {

        channel = SocketChannel.open(new InetSocketAddress(HOST,PORT));

        channel.configureBlocking(false);

        selector = Selector.open();

        channel.register(selector,SelectionKey.OP_READ);

        listenerThread = new ListenerThread();

        buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);

    }

    public void start() throws IOException {

        listenerThread.start();

        //此处是测试，不明白的可以打开测试一下
//        Scanner in = new Scanner(System.in);
//        while (in.hasNext()){
//            String msg = in.nextLine();
//            Message message = new Message();
//            message.setType(MessageType.EXAMPLE);
//            message.setMsg(msg);
//            System.out.println(message);
//            channel.write(ByteBuffer.wrap(ObjectUtils.serialize(message)));
//        }
    }

    public void shutdown(){
        listenerThread.shutdown();
    }


    /**
     * 监听服务器回执的线程，在里面补充处理回执的方法
     */
    private class ListenerThread extends Thread{

        private boolean stop = false;

        private Response response;

        private Response loginResponse;

        public Response getLoginResponse() {
            return loginResponse;
        }

        public void shutdown(){
            stop = true;
        }

        @Override
        public void run() {

            try {
                while (!stop){

                    if (selector.select()>0){

                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

                        while (iterator.hasNext()){

                            //单线程不需要修改触发标记
                            SelectionKey key = iterator.next();

                            iterator.remove();

                            //读事件就绪
                            if (key.isReadable()){
                                //读取信息
                                int len = 0;
                                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                                while ((len=channel.read(buffer))>0){
                                    buffer.flip();
                                    bos.write(buffer.array(),0,len);
                                    buffer.clear();
                                }

                                //反序列化出Response对象
                                response = ObjectUtils.deserialize(bos.toByteArray());


                                //TODO 根据不同类型写出不同的处理方法
                                switch (response.getType()){

                                    case EXAMPLE:  handleExample();   break;

                                    case DOWNLOAD: handleDownload();break;

                                    case RESPONSE_FILENAMES:handleReponseFiles(); break;
                                    
                                    case RESPONSE_ONLINE_USERS:handleResponseOnlineUsers();break;

                                    case BROADCAST:handleBroadCast();break;

                                    case TO_ONE:handleToOne();break;

                                    case IS_TRUE_USER:
                                        Login.frame.setVisible(false);
                                        loginResponse = response;
                                        createMainUI();
                                        username = Login.username;
                                        break;
                                    case USERID_IS_EXIST:
                                        RepeatID.main();
                                        break;
                                    case USER_RESISTER_SUCCESS:
                                        Register.frame.setVisible(false);
                                        Login.main(channel);
                                        break;
                                    case USER_REGISTER_FAIL:
                                        FailRegister.main();
                                        break;
                                }
                            }

                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }


        private void handleExample() {

            System.out.println("收到服务器回执："+response);

        }

        private void handleBroadCast(){

            String msg = response.getMsg();
            String sender = response.getSender();

            msg = "\n用户:"+sender + " (" + new Date().toString()+") 说：\n【"+msg+"】";

            messageArea.append(msg);
        }

        private void handleToOne(){

            String msg = response.getMsg();
            String sender = response.getSender();

            msg = "\n用户:"+sender + " (" + new Date().toString()+") 对你说：\n【"+msg+"】";

            messageArea.append(msg);

        }

        private void handleReponseFiles(){
            String[] filenames = response.getFilenames();
            FileList list = new FileList(filenames);
        }
        private void handleResponseOnlineUsers(){

            Object[] onlineUsers = response.getOnlineUsers();
            System.out.println(Arrays.toString(onlineUsers));
            String[] users = new String[onlineUsers.length];
            for (int i = 0; i < users.length; i++) {
                users[i] = (String) onlineUsers[i];
            }
            onlineUserList.setListData(users);
            count_label.setText(users.length+"人");

        }
        private void handleDownload() throws IOException {
            String target = null;
            byte[] fileContent = response.getFileContent();
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            int flag = fileChooser.showSaveDialog(fileChooser.getParent());

            //选中了保存
            if (flag==JFileChooser.APPROVE_OPTION){
                target = fileChooser.getSelectedFile().getAbsolutePath();
                System.out.println(target);
                FileOutputStream fos = new FileOutputStream(new File(target,response.getFileName()));
                fos.write(fileContent);
                fos.close();
            }
        }
    }

    public void broadCastMessage() throws IOException {

        String msg = inputArea.getText();

        inputArea.setText("");

        if (msg==null||msg.length()==0) return;

        Message message = new Message();
        message.setMsg(msg);
        message.setSender(username);
        message.setType(MessageType.BROADCAST);

        channel.write(ByteBuffer.wrap(ObjectUtils.serialize(message)));

    }

    public void requestFiles(){
        Message message = new Message();
        message.setType(MessageType.REQUEST_FILENAMES);

        try {
            channel.write(ByteBuffer.wrap(ObjectUtils.serialize(message)));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    public void requestOnlineUsers(){
        Message message = new Message();
        message.setType(MessageType.REQUEST_ONLINE_USERS);

        try {
            channel.write(ByteBuffer.wrap(ObjectUtils.serialize(message)));
        } catch (IOException e1) {
            e1.printStackTrace();
        }

    }

    public void download(String filename) throws IOException {

        Message message = new Message();

        message.setFileName(filename);

        message.setType(MessageType.DOWNLOAD);

        channel.write(ByteBuffer.wrap(ObjectUtils.serialize(message)));
    }

    public void upload(){
        JFileChooser fileChooser = new JFileChooser();
        int flag = fileChooser.showSaveDialog(fileChooser.getParent());

        //选中了保存
        if (flag==JFileChooser.APPROVE_OPTION){
            //获取用户选择的文件路径
            String path = fileChooser.getSelectedFile().getAbsolutePath();
            String filename = fileChooser.getSelectedFile().getName();
            System.out.println(path);
            System.out.println(filename);

            //读取文件到ByteArrayOutputStream
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(new File(path));
                FileChannel inChannel = fis.getChannel();
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ByteBuffer buf = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
                int length = 0;
                while ((length = inChannel.read(buf)) != -1) {
                    buf.flip();
                    bos.write(buf.array(), 0, length);
                    buf.clear();
                }

                //创建并发送Message
                Message message = new Message();
                message.setType(MessageType.UPLOAD);
                message.setFileName(filename);
                message.setFileContent(bos.toByteArray());
                System.out.println(message);
                channel.write(ByteBuffer.wrap(ObjectUtils.serialize(message)));

                //释放资源
                inChannel.close();
                fis.close();

            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    private class FileList extends JFrame {

        private String[] filenames;

        private JList<String> jList;

        private JButton b1;

        private JButton b2;

        public FileList(String[] filenames){

            this.filenames = filenames;
            //设置窗口位置和大小
            setBounds(600,100,350,460);

            setLayout(null);//设置绝对布局

            this.setTitle("服务器文件列表");     //设置窗口名字

            jList = new JList<>(filenames);
            jList.setBounds(20,10,220,380);
            add(jList);

            //设置滚动条
            JScrollPane jsp = new JScrollPane(jList);
            jsp.setBounds(20,10,220,380);
            add(jsp);

            b1 = new JButton("下载");
            b2 = new JButton("关闭");

            b1.setBounds(250,130,60,25);
            b2.setBounds(250,180,60,25);

            b1.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    int index = jList.getSelectedIndex();
                    if (index<0) {
                        JOptionPane.showMessageDialog(null,"请选择要下载的文件！");
                        return;
                    }
                    try {
                        download(filenames[index]);
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            });


            b2.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    setVisible(false);
                }
            });
            add(b1);
            add(b2);
            setVisible(true);
        }
    }

    private class ToOneArea extends JFrame{

        private String sender;

        private String receiver;

        private JTextArea inputArea;

        private JButton btn;

        public ToOneArea(String sender, String receiver){
            this.sender = sender;
            this.receiver = receiver;
            setTitle("发送信息至: "+receiver);

            //设置窗口位置和大小
            setBounds(700,200,510,340);

            setLayout(null);//设置绝对布局

            inputArea = new JTextArea();
            inputArea.setBounds(0,0,490,260);

            JScrollPane jsp1 = new JScrollPane(inputArea);
            jsp1.setBounds(0,0,490,260);
            jsp1.setViewportView(inputArea);
            add(jsp1);

            btn = new JButton("发送");
            btn.setBounds(0,260,490,35);
            add(btn);

            btn.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    String msg = inputArea.getText();
                    inputArea.setText("");
                    if (msg==null||msg.length()==0) return;

                    Message message = new Message();
                    message.setMsg(msg);
                    message.setType(MessageType.TO_ONE);
                    message.setSender(sender);
                    message.setReceiver(receiver);

                    try {
                        channel.write(ByteBuffer.wrap(ObjectUtils.serialize(message)));
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }

                }
            });

            setVisible(true);

        }
    }


    public static void main(String[] args) throws IOException {
        new Client().start();
    }
}
